package main

import (
	"fmt"
	"reflect"
)

type Student struct {
	Name  string `json:"name"`
	Age   int
	Score float64
}

func (stu Student) Say(word string) {
	fmt.Printf("%v say %v\n", stu.Name, word)
}

// 修改结构体的字段如果是string类型添加_modify后缀字符串
func reflect_with_struct_modify_field(stu interface{}) {
	// stuType := reflect.TypeOf(stu)
	stuValue := reflect.ValueOf(stu).Elem()
	num := stuValue.NumField()
	fmt.Printf("struct type is %v\n", stuValue.Type().Kind())
	fmt.Printf("struct has %d fields\n", num)
	//变量结构体的所有字段
	for i := 0; i < num; i++ {
		// 1）通过value获得类型并判断返回的还是Value
		// filedValue := stuValue.Field(i)
		// if filedValue.Type().Kind() == reflect.String {
		// 	fmt.Printf("当前字段是string类型，值为=%v", stuValue.Field(i))
		// }
		// 2）通过type获得类型判断，返回的是FieldStruct
		filedValue := stuValue.Field(i)
		if filedValue.Type().Kind() == reflect.String {
			fmt.Printf("当前字段是string类型，值为=%v\n", filedValue)
			// 修改field的值正确用法
			oldString := filedValue.String()
			// fmt.Println("旧值 = ", oldString)
			newString := oldString + "_modify"
			// fmt.Println("新值 = ", newString)
			// modifyfieldValue := stuValue.FieldByName("Name")
			modifyfieldValue := stuValue.Field(i)
			if modifyfieldValue.CanSet() {
				modifyfieldValue.SetString(newString)
			} else {
				fmt.Println("无法修改！")
			}
			fmt.Println("*******************")
			mKind := modifyfieldValue.Type().Kind()
			fmt.Println(mKind)
			mKind2 := modifyfieldValue.Elem().Type().Kind()
			fmt.Println(mKind2)
		}
	}
	fmt.Println(stu)
}

// 值传递，不传递指针
func reflect_with_struct_info(stu interface{}) {
	stuType := reflect.TypeOf(stu)
	stuValue := reflect.ValueOf(stu)

	//获取到a对应的类别
	kd := stuValue.Kind()
	//如果传入的不是struct，就退出
	if kd != reflect.Struct {
		fmt.Println("expect struct")
		return
	}

	//获取到该结构体有几个字段
	// num := stuValue.NumField()
	num := stuType.NumField()
	fmt.Printf("struct has %d fields\n", num)
	//变量结构体的所有字段
	for i := 0; i < num; i++ {
		fmt.Printf("Field %d: 值为=%v", i, stuValue.Field(i))
		//获取到struct标签, 注意需要通过reflect.Type来获取tag标签的值
		tagVal := stuType.Field(i).Tag.Get("json")
		//如果该字段于tag标签就显示，否则就不显示
		if tagVal != "" {
			fmt.Printf("Field %d: tag为=%v", i, tagVal)
		}
		fmt.Printf("\n")
	}

	//获取到该结构体有多少个方法
	// numOfMethod := stuValue.NumMethod()
	numOfMethod := stuType.NumMethod()
	fmt.Printf("struct has %d methods\n", numOfMethod)
	//var params []reflect.Value
	//方法的排序默认是按照 函数名的排序（ASCII码）
	// 如果方法没有参数就给个nil
	// val.Method(1).Call(nil) //获取到第二个方法。调用它

	//调用结构体的第1个方法Method(0)
	var params []reflect.Value //声明了 []reflect.Value
	params = append(params, reflect.ValueOf("你好！"))
	res := stuValue.Method(0).Call(params) //传入的参数是 []reflect.Value, 返回[]reflect.Value
	if res != nil {
		fmt.Println("res=", res[0].Int()) //返回结果, 返回的结果是 []reflect.Value*/
	} else {
		fmt.Println("该函数没有返回值！")
	}
}

func reflect_with_construct(stu interface{}) {
	stuType := reflect.TypeOf(stu)
	stuValue := reflect.ValueOf(stu)
	// fmt.Println(stuType.Kind().String())// ptr
	if stuType.Kind() == reflect.Ptr {
		stuValuePtr := stuValue.Elem()
		// 判断是否允许寻址、是否允许 Set 值
		if stuValuePtr.CanAddr() && stuValuePtr.CanSet() {
			// fmt.Println("可寻址！")
			// fmt.Println(stuValuePtr.Kind())//struct
			// fmt.Println(stuValuePtr.Type().Kind())//struct
			// stuValuePtr.
		}
	} else {
		fmt.Println("该类型不是指针类型！")
	}
}

func main() {
	stu := Student{"bart", 16, 99.8}
	// reflect_with_construct(&stu)
	// reflect_with_struct_info(stu)
	reflect_with_struct_modify_field(&stu)
}
